home *** CD-ROM | disk | FTP | other *** search
- /* Audit.c */
- #define PARANOIA 0
- #include <Packages.h>
- /*
- * Audit.c
- * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
- * Programmed by Martin Minow,
- * Internet: minow@apple.com
- * AppleLink: MINOW
- * Version of January 14, 1993
- *
- * Edit History
- * 93.01.09 MM First public release
- * 93.01.14 MM Think and MPW generate different record sizes; a disaster if
- * you create an Audit Record under Think and call Audit compiled
- * under MPW. Also added a test for record sizes and included
- * record size information in the AuditRecord.
- * 93.01.21 MM Fix bug in procedure name recognizer that failed for certain
- * pascal functions.
- * 93.06.10 MM Cosmetic edits for Think C 6.0. No functional changes. Text
- * was reformatted to fit into an "80-column" line.
- *
- * This library implements a drop-in logging capability for device drivers,
- * callback functions, applications, and all other Macintosh code segments.
- *
- * The overall philosophy is that an application or driver allocates a record in
- * the system heap that contains a small preamble and one or more 48 (decimal)
- * byte blocks of data. The driver logs data to the log area, where each log
- * record contains an identification value and up to four longwords of data.
- *
- * The user would then write a log control application that can turn logging on or
- * off and dump/format the data in the log. The user can also use a MacsBug dcmd
- * to display the current state of the log area.
- *
- * To add logging to a driver, add the following variable to the driver control
- * record:
- * AuditPtr auditPtr;
- * and initialize the driver log (in the driver open routine) by calling
- * devCtlEnt->auditPtr = InitAudit(
- * gestaltSelector,
- * nEntries,
- * initiallyEnabled,
- * preserveFirstFlag
- * );
- *
- * The intialization routine creates an audit record in the System Heap and
- * defines a gestalt selector with a pointer to the audit area. Once created,
- * the record remains in the heap until the Macintosh is rebooted. The
- * gestaltSelector should be unique to the driver. InitAudit need only be
- * called once by any code segment that can allocate memory (i.e., by an
- * application, INIT, or driver open function). Note that you do not
- * need to check errors: if InitAudit fails to create (or locate) the record,
- * it returns NULL and all other routines, upon receiving a NULL AuditPtr
- * argument, do nothing gracefully.
- *
- * The log record contains a fixed header with the following information:
- *
- * version.u.low The earliest version of the AuditRecord that this instance
- * of the library understands.
- * version.u.high The latest version of the AuditRecord that this instance
- * of the library understands.
- * size.auditRecord The size of the Audit Record (excluding the dummy first
- * entry). This protects against compiler alignment errors
- * (which may occur if an Audit Record is created by code
- * generated by one compiler, and accessed by code generated
- * by a different compiler.
- * size.auditEntry The size of the AuditQueueEntry. (See above.)
- * free.Queue A standard O.S. queue with available data logging records.
- * data.queue A standard O.S. queue with records waiting to be displayed
- * or written to a file.
- * lostData This counter is incremented when Audit function cannot
- * obtain a record from the free queue.
- * PSN Awaken this process when something is stored.
- * refNum a void * (longword) that may be used by the calling
- * software.
- * isLogging TRUE if logging is enabled.
- * timeAtStart GetDateTime() when the log was created.
- * ticksAtStart TickCount() when the log was created.
- * entries[] A vector of AuditEntry records.
- *
- * The audit user accesses the log record by calling functions: it should not
- * access the structure directly.
- *
- * Each log record is stored in a AuditEntry structure that contains the
- * following information:
- * tickCount The Ticks value at the time the data was collected.
- * lostData This is the number of records that were "lost" before this
- * record because there was no free entry in the queue.
- * idCode This longword identifies the log entry (i.e. who logged it).
- * format This longword describes the format of the log data.
- * data This 32 byte data area contains the log. If format is zero,
- * the data is a Str31. Otherwise, it contains up to 8
- * (sizeof data / sizeof (long)) longwords of data.
- *
- * Your application or driver calls the following functions to manage the log:
- *
- * AuditPtr InitAudit(
- * OSType gestaltSelector,
- * short nEntries
- * Boolean initiallyEnabled
- * );
- *
- * Create the log record and "publish" a Gestalt selector with a pointer to the
- * record. This is normally called by a driver when it is first opened.
- * gestaltSelector is unique to the driver. InitAudit returns a pointer to the
- * log record or NULL if it could not create the log. If the gestaltSelector was
- * already registered, it returns a pointer to the already-created log without
- * creating a new log.
- *
- * void Audit(
- * AuditPtr auditPtr,
- * OSType idCode,
- * unsigned long format,
- * ...
- * );
- *
- * Store data in the log. idCode identifies this log request: by convention, it
- * is an OSType. The format parameter simplifies display by defining the format
- * of the subsequent data, The remaining parameters are log dependent. If auditPtr
- * is NULL or logging disabled, the function does nothing, but does not fail.
- *
- * The AuditFormat macro is used to specify the format of the stored data:
- *
- * AuditFormat(arg1, arg2, ..., arg8)
- *
- * where arg1, etc. is specified by one of the following constants:
- * kAuditFormatEnd No more data
- * kAuditFormatSigned The datum is a 32-bit signed decimal integer
- * kAuditFormatUnsigned The datum is a 32-bit unsigned decimal integer
- * kAuditFormatHex The datum is a 32-bit address or 4-byte character
- * kAuditFormatAddress The datum is a 32-bit hex value (never characters)
- * kAuditFormatString The datum is a Pascal string. This must be the last
- * (or only) argument. The string will be truncated
- * to fit the remaining space. I.e. if it is the only
- * argument, a 31-byte (plus one byte count) string
- * will be stored.
- * kAuditFormatLocation This must be the last format, but there is no
- * associated value. Instead, the library stores
- * the calling function name (the MacsBug name).
- * The name will be truncated as for kAuditFormatString.
- *
- * Note that AuditFormat1, AuditFormat2, etc. macros are provided to simplify
- * access to AuditFormat().
- *
- * Important: all parameters to Audit must be longwords. Short values (such as
- * Booleans and OSErr codes) must be cast to long or unsigned long:
- * Audit(auditPtr, 'Err!', AuditFormat1(kAuditFormatSigned),
- * (long) statusError);
- * If you do not do this, the data may be logged incorrectly or, in extreme
- * cases, your program may crash.
- *
- ***
- * Boolean AuditRead(
- * AuditPtr auditPtr,
- * AuditEntry *thisAuditEntry
- * );
- * An application program calls AuditRead periodically to retrieve data from the
- * log. If there is a log entry to process, it is copied to thisAuditEntry and
- * the function returns TRUE. If it returns FALSE, there is no data waiting. This
- * function should be called from the log application's event loop. AuditRead
- * manages all log queues. Note that reading the log resets the missedDataCount.
- *
- * Each log record is time-stamped by the following algorithm:
- * GetAuditStartTimes(auditPtr, &gTimeAtStart, &gTicksAtStart);
- * if (AuditRead(auditPtr, &thisAuditEntry)) {
- * elapsedTicks = thisAuditEntry.tickCount - gTicksAtStart;
- * SecsToDate(
- * gTimeAtStart + (elapsedTicks / 60),
- * &logEntryDateString
- * );
- * printf(, ..., date.second, elapsedTicks % 60);
- * }
- * See AuditEntryFormat.c for details.
- *
- ***
- * AuditPtr GetAuditPtr(
- * OSType gestaltSelector
- * );
- *
- * Return a pointer to the common storage area, if it exists. This returns NULL
- * if there is no log "registered" by that name.
- *
- ***
- * void WakeUpAudit(
- * AuditPtr auditPtr,
- * ProcessSerialNumber *oldPSN
- * );
- *
- * Wake up this process (application) when something is logged: called by:
- * GetCurrentProcess((&oldPSN);
- * WakeUpAudit(auditPtr, &oldPSN);
- * ... logging stuff ...
- * WakeUpAudit(auditPtr, &oldPSN); // restore previous
- * ExitToShell();
- *
- ***
- * Boolean EnableAudit(
- * AuditPtr auditPtr,
- * Boolean enableLogging
- * );
- *
- * Enable or disable logging. This returns the previous logging value. At
- * initialization, a compile-time parameter sets the initial logging value.
- *
- ***
- * Boolean IsAuditEnabled(
- * AuditPtr auditPtr
- * );
- *
- * Return TRUE if auditing is enabled for this audit record.
- *
- ***
- * void GetAuditStartTimes(
- * AuditPtr auditPtr,
- * unsigned long *timeAtStart,
- * unsigned long *ticksAtStart
- * );
- * Return the times that the log was created. Using these values, the log display
- * application can time-stamp all log entries.
- *
- ***
- * void SetAuditRefNum(
- * AuditPtr auditPtr,
- * void *refNum
- * );
- * Store a user-controlled reference value. This may be coerced to any scalar
- * value (such as a memory pointer or longword).
- *
- ***
- * void *GetAuditRefNum(
- * AuditPtr auditPtr
- * );
- * Return the current user-controlled reference value. This may be coerced to any
- * scalar value (such as a memory pointer or longword). This returns zero if
- * auditPtr is NULL or no value had been stored.
- */
-
- #include "Audit.h"
- #include <Types.h>
- #include <Traps.h>
- #include <Errors.h>
- #include <Memory.h>
- #include <GestaltEqu.h>
- #include <OSUtils.h>
- #ifndef PARANOIA
- #define PARANOIA 0
- #endif
-
- /*
- * The version id's are defined as (majorVersion << 8) | minorVersion
- */
- #define kAuditEarliestVersion ((1 << 8) | 1)
- #define kAuditLatestVersion ((1 << 8) | 1)
-
- #ifndef TRUE
- #define TRUE 1
- #define FALSE 0
- #endif
- /*
- * This is Think C specific, and possibly release dependent.
- */
- #if defined(_Gestalt) == FALSE && defined(_GestaltDispatch)
- #define _Gestalt _GestaltDispatch
- #endif
-
- /*
- * Note that this file contains 68000 assembly. This must be modified when
- * the Audit Library is converted to Power PC.
- */
- /*
- * These values must match values in AuditDCMD.c
- */
- #define kAuditMagicHeaderSize 7 /* Size in longwords */
- #define kAuditMagicHeader_0 0x4E560000 /* First word in Gestalt result */
-
- #if 0
- /*
- * The following sequence was used to create the GestaltSelector stub. It is
- * never compiled directly, but was pasted into a temporary file which was
- * decompiled to get the machine code.
- */
- static pascal OSErr GestaltSelector(
- OSType selector,
- long *result
- )
- {
- asm {
- /*
- * 22 was determined by painful observation and many pleasant
- * encounters with MacsBug.
- */
- lea 22(pc),a0 /* a0 = Address of auditRecord */
- movea.l result,a1 /* a1 -> result */
- move.l a0,(a1) /* Store result */
- }
- return (noErr);
- }
- #endif
-
- /*
- * DisableInterrupts and EnableInterrupts manipulate the 68000 status register.
- * They are expanded inline. A comment consisting of *** in the left margin
- * indicates code that is run with interrupts disabled.
- *
- * unsigned short DisableInterrupts(void) {
- * asm {
- * move sr,d0
- * move #0x2600,sr
- * }
- */
- unsigned short DisableInterrupts(void) = { 0x40C0, 0x46FC, 0x2600 };
- #ifdef THINK_C
- /*
- * Since Think C has a real inline assembler, we can save one or two instructions.
- * Nerd fun.
- */
- #define EnableInterrupts(saveSR) asm { \
- move saveSR,sr \
- }
- #else
- /*
- * Even though the saveSR argument is a short, we must declare the function as a
- * long so that both Think and MPW generate the same code. Otherwise, Think pushes
- * a short and 2(sp) addresses the wrong value. (Guess how I discovered this.)
- * Using the Think C inline defined above also eliminates the problem.
- *
- * void EnableInterrupts(unsigned long saveSR) {
- * asm {
- * move 2(sp),sr
- * }
- */
- void EnableInterrupts(unsigned long saveSR) = { 0x46EF, 0x0002 };
- #endif
-
- /*
- * This inline copies the value of A6 (the frame pointer) to D0. It is needed in
- * order to get the caller's function name.
- */
- unsigned long *GetA6(void) = { 0x200E }; /* move.l A6,D0 */
-
- #define LOG (*auditPtr)
- #define ENTRY (entryPtr->theEntry)
-
- static void StoreString(
- const StringPtr theString, /* Source, length may be wrong */
- unsigned short stringLength, /* Purported actual length */
- unsigned short maxLength, /* Maximum destination length */
- StringPtr result /* Destination pointer */
- );
- static AuditQueueEntryPtr GetQueueEntry(
- QHdrPtr theQueue
- );
- #define PutQueueEntry(theQueue, theEntry) do { \
- Enqueue((QElemPtr) &theEntry->qLink, theQueue); \
- } while (0)
-
- static Boolean TrapAvailable(
- short theTrap
- );
- static void GetCallerName(
- unsigned long *destPtr,
- unsigned short maxString,
- unsigned long *a6
- );
-
- /*
- * InitAudit
- */
- AuditPtr
- InitAudit(
- OSType gestaltSelector,
- unsigned short nEntries,
- Boolean initiallyEnabled,
- Boolean preserveFirst
- )
- {
- register unsigned long *recordPtr;
- register AuditPtr auditPtr;
- register AuditQueueEntryPtr queueEntryPtr;
- Size logRecordSize;
- short i;
- OSErr status;
-
- /*
- * This will not compile on either Think or MPW if the sizes are correct.
- * Unfortunately, sizeof cannot be incorporated into a #define, so we
- * have to do this at execution time.
- */
- if (sizeof (AuditEntry) != kSizeofAuditEntry)
- DebugStr("\pAuditEntry size error");
- if (sizeof (AuditQueueEntry) != kSizeofAuditQueueEntry)
- DebugStr("\pAuditQueueEntry size error");
- if (sizeof (AuditRecord) != kSizeofAuditRecord)
- DebugStr("\pAuditRecord size error");
- /*
- * Presuppose problems and exit if the system is so old that it doesn't
- * even support Gestalt. This is slightly overkill, as the current
- * Gestalt glue does the TrapAvailable check for us.
- */
- auditPtr = NULL;
- if (TrapAvailable(_Gestalt) == FALSE)
- goto exit;
- logRecordSize = sizeof (AuditRecord)
- + (kAuditMagicHeaderSize * sizeof (long))
- + ((nEntries - 1) * sizeof (AuditQueueEntry));
- #if PARANOIA > 1
- {
- Str255 foo;
- NumToString(sizeof (AuditQueueEntry), foo);
- DebugStr(foo);
- NumToString(logRecordSize, foo);
- DebugStr(foo);
- }
- #endif
- recordPtr = (unsigned long *) NewPtrSysClear(logRecordSize);
- if (recordPtr == NULL)
- goto exit;
- auditPtr = (AuditPtr) &recordPtr[kAuditMagicHeaderSize];
- /*
- * The code for the Gestalt Selector function precedes the actual audit
- * record. Gestalt calls this function when a program calls GetAuditPtr
- * for our selector. The function returns the address of the AuditRecord
- * as the selector value. This code will need revision when run on
- * non-Motorola cpu's. See the definition of GestaltSelector above for
- * the original code.
- *
- * asm {
- * link a6,#0000 Create stack frame 0x4E56 0x0000
- * lea 22(pc),a0 a0 -> auditRecord 0x41FA 0x0016
- * movea.l 8(a6),a1 a1 -> response 0x226E 0x0008
- * move.l a0,(a1) Store result 0x2288
- * clr.w 8(a6) Result := noErr 0x426E 0x0010
- * unlk a6 Delete stack frame 0x4E5E
- * movea.l (a7)+,a0 Pop return address 0x205F
- * addq.l #0x8,a7 Clear stack params 0x508F
- * jmp (a0) Return to caller 0x4ED0
- * dc.w 0 Fake MacsBug name 0x0000
- * ** AuditRecord starts here **
- * };
- */
- #if kAuditMagicHeader_0 != 0x4E560000
- << error, the following won't work: see AuditDCMD.c >>
- #endif
- recordPtr[0] = 0x4E560000;
- recordPtr[1] = 0x41FA0016;
- recordPtr[2] = 0x226E0008;
- recordPtr[3] = 0x2288426E;
- recordPtr[4] = 0x00104E5E;
- recordPtr[5] = 0x205F508F;
- recordPtr[6] = 0x4ED00000; /* Pad with fake MacsBug name */
- #if kAuditMagicHeaderSize != 7
- << error, the above won't work: see AuditDCMD.c >>
- #endif
- /*
- * If this machine has a data/instruction cache, flush it so that we
- * cannot execute stale data.
- */
- if (TrapAvailable(_HWPriv)) {
- FlushDataCache();
- FlushInstructionCache();
- }
- /*
- * Add this as a gestalt. Errors present problems: either this was already
- * added (no big deal), or the luser is trying to redefine a "normal"
- * Gestalt. We check by trying to get the AuditPtr and hope that
- * GetAuditPtr's internal checks ferret out an attempt to redefine a
- * normal Gestalt.
- */
- status = NewGestalt(gestaltSelector, (ProcPtr) recordPtr);
- if (status == gestaltDupSelectorErr) {
- /*
- * Trouble! this has already been added! Your program
- * may eventually crash if the gestalt is not an audit
- * record: see GetAuditPtr() for the safety checks.
- */
- DisposPtr((Ptr) auditPtr);
- auditPtr = GetAuditPtr(gestaltSelector);
- goto exit;
- }
- else if (status != noErr) {
- /*
- * Something is seriously out of order. Just return NULL.
- */
- DisposPtr((Ptr) recordPtr);
- auditPtr = NULL;
- goto exit;
- }
- /*
- * Setup the rest of the AuditRecord.
- */
- LOG.version.u.low = kAuditEarliestVersion;
- LOG.version.u.high = kAuditLatestVersion;
- LOG.recordSize = kAuditRecordSize;
- GetDateTime(&LOG.timeAtStart);
- LOG.ticksAtStart = TickCount();
- LOG.PSN.lowLongOfPSN = kNoProcess;
- /*
- * Ok, so far. Now build the initial free queue.
- */
- queueEntryPtr = &LOG.entries[0];
- for (i = 0; i < nEntries; i++) {
- PutQueueEntry(&LOG.free.queue, queueEntryPtr);
- ++queueEntryPtr;
- }
- exit: EnableAudit(auditPtr, initiallyEnabled);
- PreserveAudit(auditPtr, preserveFirst);
- return (auditPtr);
- }
-
- /*
- * This function logs data if logging is enabled.
- *
- * auditPtr As returned by InitAudit. If NULL, nothing is logged.
- * idCode A user-controlled value, by convention an OSType (4-byte
- * character), that identifies this entry. The display program
- * prints it.
- * format The format of the remaining data: kAuditFormatString if the
- * datum is a pascal string, otherwise, it is as created by the
- * AuditFormat macro.
- * ... Additional data as needed. These values must be cast to
- * longwords to prevent ThinkC/MPW incompatibilities.
- */
- void
- Audit(
- register AuditPtr auditPtr,
- OSType idCode,
- unsigned long format,
- ...
- )
- {
- unsigned short saveSR;
- va_list argPtr;
- register unsigned long *destPtr;
- unsigned long tickCount;
- unsigned short maxString;
- unsigned short thisFormat;
- StringPtr theString;
- register AuditQueueEntryPtr entryPtr;
- register AuditQueueEntryPtr nextPtr;
-
- tickCount = TickCount();
- if (auditPtr != NULL && (LOG.flags & kAuditEnabledMask) != 0) {
- entryPtr = GetQueueEntry(&LOG.free.queue);
- /*
- * Note: *** in the left-most column indicates code that is executed
- * with interrupts disabled.
- */
- saveSR = DisableInterrupts();
- /***/ if (entryPtr == NULL) {
- /***/ ++LOG.lostData;
- /***/ EnableInterrupts(saveSR);
- if ((LOG.flags & kAuditPreserveFirstMask) == 0) {
- /*
- * We really want to log the latest entry. Remove the first
- * "to be displayed" entry. If successful, it will get the new
- * record and the next "to be displayed" entry, if any, gets
- * the lost data counter.
- */
- entryPtr = GetQueueEntry(&LOG.data.queue);
- if (entryPtr == NULL)
- ; /* goto return */
- else {
- /***/ saveSR = DisableInterrupts();
- /***/ LOG.lostData += ENTRY.lostData;
- /***/ nextPtr = (AuditQueueEntryPtr) LOG.data.queue.qHead;
- /***/ if (nextPtr != NULL) {
- /***/ nextPtr->theEntry.lostData += LOG.lostData;
- /***/ LOG.lostData = 0;
- /***/ }
- /***/ goto returnThisEntry;
- }
- }
- }
- /***/ else {
- returnThisEntry:
- /***/ ENTRY.lostData = LOG.lostData;
- /***/ LOG.lostData = 0;
- /***/ EnableInterrupts(saveSR);
- ENTRY.tickCount = tickCount;
- ENTRY.idCode = idCode;
- ENTRY.format = format;
- va_start(argPtr, format);
- destPtr = ENTRY.data;
- maxString = sizeof (ENTRY.data);
- while (maxString > 0) {
- thisFormat = format & kAuditFormatMask;
- switch (thisFormat) {
- case kAuditFormatEnd:
- goto exitLoop;
- case kAuditFormatString:
- theString = va_arg(argPtr, StringPtr);
- StoreString(
- theString,
- theString[0],
- maxString,
- (StringPtr) destPtr
- );
- goto exitLoop;
- case kAuditFormatLocation:
- GetCallerName(destPtr, maxString, GetA6());
- goto exitLoop;
- default:
- *destPtr++ = va_arg(argPtr, unsigned long);
- format >>= kAuditFormatShift;
- maxString -= sizeof (unsigned long);
- break;
- }
- }
- exitLoop: va_end(nextArg);
- PutQueueEntry(&LOG.data.queue, entryPtr);
- if (LOG.PSN.highLongOfPSN != 0
- || LOG.PSN.lowLongOfPSN != kNoProcess)
- (void) WakeUpProcess(&LOG.PSN);
- }
- }
- #undef DATA
- }
-
- /*
- * The following functions are normally called by the display application:
- */
-
- /*
- * Return a pointer to the log area, or NULL if there is none. Note that we make
- * a few sanity checks to better ensure that the gestaltSelector actually refers
- * to a Audit record.
- */
- AuditPtr
- GetAuditPtr(
- OSType gestaltSelector
- )
- {
- auto long gestaltResponse;
- register AuditPtr auditPtr;
-
- if (TrapAvailable(_Gestalt) == FALSE
- || Gestalt(gestaltSelector, &gestaltResponse) != noErr
- || gestaltResponse == 0
- || (gestaltResponse & 0x3) != 0)
- auditPtr = NULL;
- else {
- auditPtr = (AuditPtr) gestaltResponse;
- /*
- * Sanity check: make sure the result is really an Audit Record and
- * we can deal with the library release that created it.
- */
- if (((unsigned long *) auditPtr)[-kAuditMagicHeaderSize]
- != kAuditMagicHeader_0
- || LOG.version.u.low < kAuditEarliestVersion
- || LOG.version.u.high > kAuditLatestVersion
- || LOG.recordSize != kAuditRecordSize)
- auditPtr = NULL;
- }
- return (auditPtr);
- }
-
- /*
- * Awaken a specified process. Call by the following sequence:
- * GetCurrentProcess(&oldPSN); // process to awaken
- * WakeUpAudit(auditPtr, &oldPSN);
- * The previous process is now stored in oldPSN)
- * ... display the log ...
- * WakeUpAudit(auditPtr, &oldPSN); // restore old
- * ExitToShell();
- */
- void
- WakeUpAudit(
- register AuditPtr auditPtr,
- ProcessSerialNumber *oldPSN
- )
- {
- ProcessSerialNumber previousPSN;
- short saveSR;
- long gestaltResult;
- OSErr status;
-
- if (auditPtr != NULL) {
- status = Gestalt(gestaltOSAttr, &gestaltResult);
- if (status != noErr
- || (gestaltResult & (1 << gestaltLaunchControl)) == 0) {
- oldPSN->highLongOfPSN = 0;
- oldPSN->lowLongOfPSN = kNoProcess;
- }
- /***/ saveSR = DisableInterrupts();
- /***/ previousPSN = LOG.PSN;
- /***/ LOG.PSN = *oldPSN;
- /***/ EnableInterrupts(saveSR);
- *oldPSN = previousPSN;
- }
- }
-
- /*
- * Enable/disable logging. Returns the old logging state.
- */
- Boolean
- EnableAudit(
- register AuditPtr auditPtr,
- Boolean enableLogging
- )
- {
- Boolean oldLogState;
- short saveSR;
-
- if (auditPtr == NULL)
- oldLogState = FALSE;
- else {
- /***/ saveSR = DisableInterrupts();
- /***/ oldLogState = (LOG.flags & kAuditEnabledMask) != 0;
- /***/ LOG.flags &= ~kAuditEnabledMask;
- /***/ if (enableLogging)
- /***/ LOG.flags |= kAuditEnabledMask;
- /***/ EnableInterrupts(saveSR);
- }
- return (oldLogState);
- }
-
- /*
- * Return the value of the Audit enable flag. Returns FALSE if auditPtr is NULL
- * or auditing is disabled.
- */
- Boolean
- IsAuditEnabled(
- AuditPtr auditPtr
- )
- {
- if (auditPtr == NULL)
- return (FALSE);
- else {
- return ((LOG.flags & kAuditEnabledMask) != 0);
- }
- }
-
- /*
- * Enable/disable logging. Returns the old logging state.
- */
- Boolean
- PreserveAudit(
- register AuditPtr auditPtr,
- Boolean preserveFirst
- )
- {
- Boolean oldPreserveState;
- short saveSR;
-
- if (auditPtr == NULL)
- oldPreserveState = FALSE;
- else {
- /***/ saveSR = DisableInterrupts();
- /***/ oldPreserveState = (LOG.flags & kAuditPreserveFirstMask) != 0;
- /***/ LOG.flags &= ~kAuditPreserveFirstMask;
- /***/ if (preserveFirst)
- /***/ LOG.flags |= kAuditPreserveFirstMask;
- /***/ EnableInterrupts(saveSR);
- }
- return (oldPreserveState);
- }
-
- /*
- * Get the time that the log record was created. This is used to time-stamp log
- * entries. Since the time-stamp is created only when the log is created (and
- * before it is visible through Gestalt), we don't need to lock out interrupts.
- */
- void
- GetAuditStartTimes(
- register AuditPtr auditPtr,
- unsigned long *timeAtStart,
- unsigned long *ticksAtStart
- )
- {
- if (auditPtr == NULL) {
- *timeAtStart = 0;
- *ticksAtStart = 0;
- }
- else {
- *timeAtStart = LOG.timeAtStart;
- *ticksAtStart = LOG.ticksAtStart;
- }
- }
-
- /*
- * Read the next log entry. This returns a copy of the log entry, if one is
- * available and returns TRUE. This function manages all log queues. If no entry
- * is available, or logging is disabled, the function returns FALSE.
- */
- Boolean
- ReadAudit(
- register AuditPtr auditPtr,
- AuditEntryPtr thisAuditEntry
- )
- {
- register AuditQueueEntryPtr entryPtr;
-
- if (auditPtr == NULL)
- return (FALSE);
- else {
- entryPtr = GetQueueEntry(&LOG.data.queue);
- if (entryPtr == NULL)
- return (FALSE);
- else {
- *thisAuditEntry = ENTRY;
- PutQueueEntry(&LOG.free.queue, entryPtr);
- return (TRUE);
- }
- }
- }
-
- /*
- * Store a user-controlled reference value. This may be coerced to any scalar
- * value (such as a memory pointer or longword). SetAuditRefNum returns the
- * current value of the refNum, or zero if auditPtr is NULL.
- */
- void
- *SetAuditRefNum(
- AuditPtr auditPtr,
- void *refNum
- )
- {
- void *result;
- short saveSR;
-
- if (auditPtr == NULL)
- result = NULL;
- else {
- /***/ saveSR = DisableInterrupts();
- /***/ result = LOG.refNum;
- /***/ LOG.refNum = refNum;
- /***/ EnableInterrupts(saveSR);
- }
- return (result);
- }
-
- /*
- * Return the current user-controlled reference value. This may be coerced to any
- * scalar value (such as a memory pointer or longword). This returns zero if
- * auditPtr is NULL or no value had been stored.
- */
- void *
- GetAuditRefNum(
- AuditPtr auditPtr
- )
- {
- /*
- * Note that we assume that we can retrieve LOG.refNum without
- * interference from interrupt routines. Is this reasonable?
- */
- return ((auditPtr == NULL) ? NULL : LOG.refNum);
- }
-
- /*
- * Remove the first entry from the queue, return NULL on failure.
- */
- static AuditQueueEntryPtr
- GetQueueEntry(
- QHdrPtr theQueue
- )
- {
- register QElemPtr qElemPtr;
-
- if ((qElemPtr = theQueue->qHead) != NULL) {
- if (Dequeue(qElemPtr, theQueue) != noErr)
- qElemPtr = NULL;
- }
- return ((AuditQueueEntryPtr) qElemPtr);
- }
-
- /*
- * TrapAvailable (see Inside Mac VI 3-8)
- */
- #define NumToolboxTraps() ( \
- (NGetTrapAddress(_InitGraf, ToolTrap) \
- == NGetTrapAddress(0xAA6E, ToolTrap)) \
- ? 0x200 : 0x400 \
- )
- #define GetTrapType(theTrap) ( \
- ((theTrap) & 0x800 != 0) ? ToolTrap : OSTrap \
- )
-
- static Boolean
- TrapAvailable(
- short theTrap
- )
- {
- TrapType trapType;
-
- trapType = GetTrapType(theTrap);
- if (trapType == ToolTrap) {
- theTrap &= 0x07FF;
- if (theTrap >= NumToolboxTraps())
- theTrap = _Unimplemented;
- }
- return (
- NGetTrapAddress(theTrap, trapType)
- != NGetTrapAddress(_Unimplemented, ToolTrap)
- );
- }
-
- /*
- * GetCallerName peeks up the calling chain list to the Audit function caller. It
- * then scans through the function code to locate the start and end of the
- * function. The end of the function is followed by the MacsBug name (maybe).
- * This is based on ShowStackChain.c by Greg Anderson greggor@apple.com
- */
- static void
- GetCallerName(
- unsigned long *destPtr,
- unsigned short maxString,
- unsigned long *a6
- )
- {
- unsigned long returnAddress;
- register unsigned short *pc;
- short maxSearch;
- unsigned short length;
- Str31 tempString;
- unsigned long pcOffset;
- #define pcByte ((unsigned char *) pc)
-
- returnAddress = a6[1]; /* Return address to Audit caller */
- /*
- * First, look forwards for the symbol name
- */
- pc = (unsigned short *) returnAddress;
- tempString[0] = 0;
- for (maxSearch = 0; maxSearch < 0x4000; maxSearch++, pc++) {
- if (pc[0] == 0x4E56) /* link */
- break; /* We've gone too far */
- if (pc[0] == 0x4E75 /* rts */
- || pc[0] == 0x4ED0 /* jmp (a0) */
- || pc[0] == 0x4E74) { /* rtd #N */
- if (pc[0] == 0x4E74) /* If rtd, skip over displacement */
- pc++;
- pc++; /* Skip over return instruction */
- if (pc[0] != 0x4E56) { /* Link instruction means no name */
- /*
- * pc now points to the word following the RTS. Use the
- * MacsBug naming conventions to determine the function name,
- * if any. See Appendix D in the MacsBug User's Guide.
- */
- length = pcByte[0] & 0x7F;
- if (length >= 0x20 && length <= 0x7F) {
- /*
- * Fixed length format: first byte is in the range 0x20
- * through 0x7F, the high bit may or may not be set.
- */
- if ((pcByte[1] & 0x80) != 0) {
- /*
- * Pascal 16-byte class name format. The string is
- * stored "method" "class" (each takes 8 bytes) --
- * MacsBug swaps the order and inserts a '.'
- * (Warning, this is untested.)
- */
- BlockMove(&pcByte[0], &tempString[10], 8);
- BlockMove(&pcByte[7], &tempString[1], 8);
- tempString[10] &= ~0x80;
- tempString[11] &= ~0x80;
- tempString[9] = '.';
- tempString[0] = 17;
- }
- else {
- /*
- * Pascal 8 byte format.
- */
- BlockMove(&pcByte[0], &tempString[1], 8);
- tempString[0] = 7;
- }
- }
- else if (pcByte[0] >= 0x80 && pcByte[0] <= 0x9F) {
- /*
- * Variable-length string format. If the length byte is
- * zero after removing the flag bit, the next byte has
- * the true (Str255) length.
- */
- if (length == 0) {
- /*
- * Step over the flag. Note that pc and pcByte are the
- * same variable (pc is a "short *" while pcByte is a
- * "char *"). The next statement adds 1 to pc in a way
- * that doesn't cause ANSI compilers to lose their
- * electronic lunches.
- */
- pc = (unsigned short *) &pcByte[1];
- length = pcByte[0];
- }
- StoreString(
- pcByte,
- length,
- sizeof (tempString) - 1,
- tempString
- );
- }
- else {
- /*
- * Something isn't understandable. Do nothing. This will
- * be caught by the "if there is no symbol" test below.
- */
- }
- }
- break;
- }
- }
- /*
- * If there is no symbol name, don't search for an offset.
- */
- pcOffset = 0;
- if (tempString[0] != 0) {
- pc = (unsigned short *) returnAddress;
- for (maxSearch = 0; maxSearch < 0x4000; maxSearch++) {
- if (pc[0] == 0x4E56) { /* link instruction */
- pcOffset =
- ((unsigned long) returnAddress)
- - ((unsigned long) pc);
- break;
- }
- --pc;
- }
- }
- if (pcOffset == 0 || tempString[0] == 0) {
- /*
- * Since we don't have a MacsBug name, set the offset value to zero
- * (this will act as a signal) and store the return address in the
- * second data value.
- */
- if (maxString >= sizeof (unsigned long)) {
- *destPtr++ = 0;
- maxString -= sizeof (unsigned long);
- }
- if (maxString >= sizeof (unsigned long)) {
- *destPtr++ = returnAddress;
- maxString -= sizeof (unsigned long);
- }
- }
- else {
- if (maxString >= sizeof (unsigned long)) {
- *destPtr++ = pcOffset;
- maxString -= sizeof (unsigned long);
- StoreString(
- tempString,
- tempString[0],
- maxString,
- (StringPtr) destPtr
- );
- }
- }
- #undef pcByte
- }
-
- /*
- * Copy the source string to the destination, respecting the maximum size.
- */
- static void
- StoreString(
- const StringPtr theString,
- unsigned short stringLength,
- unsigned short maxLength,
- StringPtr result
- )
- {
- if (stringLength >= maxLength)
- stringLength = maxLength - 1;
- BlockMove(theString, result, stringLength + 1);
- result[0] = stringLength;
- }
-
-
-